"""
《邢不行-2020新版|Python数字货币量化投资课程》
无需编程基础，助教答疑服务，专属策略网站，一旦加入，永续更新。
课程详细介绍：https://quantclass.cn/crypto/class
邢不行微信: xbx3636
本程序作者: 邢不行

# 课程内容
    币安期现套利
    1. 先将现货中放入usdt，币本位合约中放入若干symbol的币
    2. 获取现货symbol价格，获取币本位合约symbol价格，计算利差
    3. 当利差满足要求，分别计算币本位合约开空的数量及买入现货的数量，
"""

from Function import *
from Config import *
from Market import *


def close_position(symbol_position_list):
    # ===获取已保存的现货和合约交易对精度信息
    symbol_precision_info = np.load('../data/symbol_precision_info.npy', allow_pickle=True).item()

    for symbol in symbol_position_list:
        if fetch_symbol_assets_num(symbol, 'coin') > 0:

            spot_symbol = symbol + 'USDT'  # 现货交易对名称
            future_symbol = symbol + future_date  # 合约交易对名称

            # 获取现货交易对精度
            spot_price_precision, spot_quantity_precision = symbol_precision_info[spot_symbol]
            # 获取币本位合约交易对精度
            future_price_precision, future_quantity_precision = symbol_precision_info[future_symbol]

            # 获取现货买一数据。因为现货是卖出，取买一。
            # GET /api/v3/ticker/bookTicker
            spot_bull1_price = float(exchange.publicGetTickerBookTicker(
                params={'symbol': spot_symbol})['bidPrice'])  # 无需round()，返回的数据自带精度

            # 获取期货卖一数据。因为期货是，买入，取卖一。
            # GET /dapi/v1/ticker/bookTicker
            future_sell1_price = float(exchange.dapiPublicGetTickerBookTicker(
                params={'symbol': future_symbol})[0]['askPrice'])  # 无需round()，返回的数据自带精度

            print(f"现货中{symbol}的数量为：{fetch_symbol_assets_num(symbol, 'spot'):.3f}")
            print(f"合约中{symbol}的数量为：{fetch_symbol_assets_num(symbol, 'coin'):.3f}")

            # 计算价差
            r = round(future_sell1_price / spot_bull1_price - 1, 4)
            # %.2f为保留2位小数；%.2f%%为保留2位小数并按%输出
            print(f'现货价格：%.{spot_price_precision}f，期货价格：%.{future_price_precision}f，价差：%.2f%%' %
                  (spot_bull1_price, future_sell1_price, r * 100))

            # ===判断价差是否满足要求
            if r > r_close_threshold:
                print('利差小于目标阀值，不出金\n')
            else:
                print('利差大于目标阀值，开始出金：在币本位合约平空后，将币从合约转入现货卖出\n')

                # 获取合约账户中symbol的持仓合约张数，若为做空，则为负数，取绝对值
                future_contract_num = abs(fetch_symbol_positions_num(symbol, 'coin'))

                # 计算平空合约的数量
                future_coin_num = future_contract_num * fetch_contact_size(symbol) / future_sell1_price  # 合约张数对应币的数量
                # 现货需要卖出的币的数量
                future_fee = future_coin_num * future_fee_rate  # 合约手续费
                # 现货需要卖出币的数量 = 合约平空币的数量 - 合约平空手续费
                # 公式为：(spot_amount + future_fee) * (1 - spot_fee_rate) = future_coin_num
                spot_amount = future_coin_num / (1 - spot_fee_rate) - future_fee

                print(f'交易计划：平空合约张数：%.f；对应币数量：%.3f；需要卖出现货数量：%.3f\n' %
                      (future_contract_num, future_coin_num, spot_amount))

                # 合约平空
                future_price = round(future_sell1_price * (1 + price_slippage), future_price_precision)
                future_sell_info = binance_future_place_order(exchange=exchange, future_symbol=future_symbol,
                                                              long_or_short='平空', price=future_price,
                                                              amount=future_contract_num)
                print(f'合约下单信息：\n{future_sell_info}')
                time.sleep(1)

                # 获取合约账户中待转账币的数量
                print(f"合约中{symbol}的币数量为：{fetch_symbol_assets_num(symbol, 'coin')}")
                _ = future_coin_num - future_fee
                # 将合约账户平空的币转移到现货账户
                binance_account_transfer(exchange=exchange, symbol=symbol, amount=future_coin_num - future_fee,
                                         from_account='合约', to_account='现货')

                # 重新获取现货买一数据。因为现货是卖出，取买一
                # GET /api/v3/ticker/bookTicker
                spot_bull1_price_2 = float(exchange.publicGetTickerBookTicker(
                    params={'symbol': spot_symbol})['bidPrice'])  # 无需round()，返回的数据自带精度
                # 现货卖出
                spot_price_2 = round(spot_bull1_price_2 * (1 - price_slippage), spot_price_precision)
                spot_buy_info = binance_spot_place_order(exchange=exchange, spot_symbol=spot_symbol,
                                                         long_or_short='卖出', price=spot_price_2,
                                                         amount=round(spot_amount, spot_quantity_precision))
                print(f'现货下单信息：\n{spot_buy_info}')

                # ===循环结束
                print('*' * 20, '本次循环结束，暂停', '*' * 20, '\n')
                time.sleep(2)


if __name__ == '__main__':
    # try:
    open_position_symbol_list = np.load('open_position_symbol_list.npy', allow_pickle=True).item()
    if open_position_symbol_list:  # 期限套利程序有持仓时，自动平仓程序才启动
        # ===判断并修改为单向持仓
        # if_oneway_mode()
        # ===获取并保存现货和合约交易对精度信息
        # fetch_save_symbol_precision()
        # while True:
        close_position(open_position_symbol_list)
    # except Exception as e:
    #     print('系统出错，10s之后重新运行，出错原因：' + str(e))
    #     print(e)
    #     time.sleep(2)
